package wangwenjun.phase1.reference;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import util.ThreadUtil;
import wangwenjun.phase1.reference.soft.SoftLRUCache;
import wangwenjun.phase1.reference.strong.LRUCache;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.stream.IntStream;

/**
 * @author ChangLiang
 * @date 2020/6/16
 */
@Slf4j
public class AppTest {

    @Test
    @DisplayName("basic strong reference use")
    public void test() {
        LRUCache<String, Reference> cache = new LRUCache<>(5, key -> new Reference());
        cache.get("A");
        cache.get("B");
        cache.get("C");
        cache.get("D");
        cache.get("E");
        // E->D->C->B->A
        cache.get("F");
        System.out.println(cache.toString());
    }

    @Test
    @DisplayName("test strong reference OOM")
    public void test2() throws InterruptedException {
        // 获取使用的gc collector
        // output:
        // PS Scavenge
        // PS MarkSweep
        List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
        for (GarbageCollectorMXBean bean : beans) {
            log.warn(bean.getName());
        }
        ThreadUtil.sleepForMilliSeconds(10_000);
        LRUCache<String, Reference> cache = new LRUCache<>(200, key -> new Reference());
        IntStream.range(0, Integer.MAX_VALUE).forEach(i -> {
            cache.get(String.valueOf(i));
            ThreadUtil.sleepForMilliSeconds(1_000);
            log.info("Thread:{} store #{} reference into lru-cache", Thread.currentThread(), i);
        });
        Thread.currentThread().join();
    }


    @Test
    @DisplayName("test soft reference OOM")
    public void test3() throws InterruptedException {
        // 获取使用的gc collector
        // output:
        // PS Scavenge
        // PS MarkSweep
        List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();
        for (GarbageCollectorMXBean bean : beans) {
            log.warn(bean.getName());
        }
        ThreadUtil.sleepForMilliSeconds(10_000);
        SoftLRUCache<String, Reference> cache = new SoftLRUCache<>(200, key -> new Reference());
        IntStream.range(0, Integer.MAX_VALUE).forEach(i -> {
            cache.get(String.valueOf(i));
            ThreadUtil.sleepForMilliSeconds(1_000);
            log.info("Thread:{} store #{} reference into lru-cache", Thread.currentThread(), i);
        });
        Thread.currentThread().join();
    }
    
    @Test
    @DisplayName("test weak reference")
    public void test4() throws InterruptedException {
        Reference ref = new Reference();
        WeakReference<Reference> reference = new WeakReference<>(ref);
        ref = null;
        System.gc();
    }

    @Test
    @DisplayName("test ReferenceQueue")
    public void test5() throws InterruptedException {
        // 被垃圾回收的Reference 会被加入与之关联的Queue 中
        ReferenceQueue<Reference> queue = new ReferenceQueue<>();
        Reference ref = new Reference();
        // 定义WeakReference 并且指定关联的Queue
        WeakReference<Reference> weakReference = new WeakReference<>(ref, queue);

        ref = null;
        log.warn("WeakReference value:{}", weakReference.get().toString());

        // 手动执行gc 操作
        System.gc();

        ThreadUtil.sleepForMilliSeconds(1_000);
        // remove 方法是阻塞方法
        java.lang.ref.Reference<? extends Reference> queuedRef = queue.remove();
        // 被垃圾回收之后，会从队列中获得
        log.info("reference:{} in queue",queuedRef);
    }

}
